/*
 * BAT_Annotation.c
 *
 * Data structures for the BAT framework.
 *
 * $Id: BAT_Annotation.c 927 2006-09-03 06:17:24Z rpz $
 *
 * Bob Zimmermann <rpz@cse.wustl.edu> Totally free.
 *
 */

/*
 * notes to self: - all structures are responsible for the memory of their
 * immediate children - all structures are responsible for informing their
 * parents of changes - public add/delete functions immediately update
 * parents - private add/delete functions only set the resized bit - no
 * passed chunks are co-opted by the data structure, always copied. - all
 * invariants are restored before returning a public function.
 *
 * basic functions: - find {by_geneid,by_txid} - add (add without putting it in
 * its place -- private) - insert (add and put it in its place) - update (for
 * use after adds -- private)
 */

/*
 * to save memory: - eliminate keyed sets - have the children be NULL if it's
 * a leaf (i.e. a feature)
 */

/*
 * TODO: we should const all the getters.
 */
#include "BAT_Annotation.h"
#include "BAT_private.h"
#include "mem.h"

darray_ops_t BAT_Owner_Ops[] = {
  {(darray_copy_op) & BAT_Gene_copy, &BAT_Gene_order, &BAT_Gene_id_cmp,
   (darray_delete_op) & BAT_Gene_delete}
  ,
  {(darray_copy_op) & BAT_Transcript_copy, &BAT_Gene_order,
   &BAT_Transcript_id_cmp, (darray_delete_op) & BAT_Transcript_delete}
  ,
  {(darray_copy_op) & BAT_Feature_copy, &BAT_Feature_order, NULL,
   (darray_delete_op) & BAT_Feature_delete}
  ,
  {(darray_copy_op) & BAT_Free_Feature_copy, &BAT_Free_Feature_order, NULL,
   (darray_delete_op) & BAT_Free_Feature_delete}
  ,
  {(darray_copy_op) & BAT_Attribute_copy, &BAT_Attribute_order,
   &BAT_Attribute_key_cmp, (darray_delete_op) & BAT_Attribute_delete}
  ,
  {(darray_copy_op) & BAT_Util_copy_string, NULL, NULL, mem_free}
  ,
  {NULL, NULL, NULL, NULL}
};

darray_ops_t BAT_Passive_Ops[] = {
  {NULL, &BAT_Gene_order, &BAT_Gene_id_cmp,
   (darray_delete_op) & BAT_Gene_delete}
  ,
  {NULL, &BAT_Gene_order, &BAT_Transcript_id_cmp, NULL}
  ,
  {NULL, &BAT_Feature_order, NULL, NULL}
  ,
  {NULL, &BAT_Free_Feature_order, NULL, NULL}
  ,
  {NULL, &BAT_Attribute_order, &BAT_Attribute_key_cmp, NULL}
  ,
  {NULL, NULL, NULL, mem_free}
  ,
  {NULL, NULL, NULL, NULL}
};

keyed_set_ops_t BAT_KS_Ops = { (keyed_set_copy_op) darray_copy,
  (keyed_set_delete_op) darray_delete
};

/*
 * --- BAT_Base ---
 */

BAT_Base *
_BAT_Base_init(size_t sz, int nallocs, ...)
{
  va_list ap;
  BAT_Base *obj;
  BAT_Allocator_ID child_id, id;
  int i;

  obj = malloc(sz);
  obj->_parent = NULL;

  va_start(ap, nallocs);
  child_id = va_arg(ap, BAT_Allocator_ID);
  obj->_children = NULL;
  obj->_allocators = NULL;

  if (nallocs == 1) {
    obj->_allocators = NULL;
    obj->_children = _BAT_Util_direct_renew_alloc(obj->_children, child_id, 1);
  }
  else if (nallocs > 1) {
    obj->_allocators = keyed_set_new(nallocs, keyed_set_ops_copy(&BAT_KS_Ops));
    _BAT_Util_renew_alloc(obj->_allocators, child_id, 1);
    obj->_children = keyed_set_get(obj->_allocators, child_id);

    for (i = 1; i < nallocs; i++) {
      id = va_arg(ap, BAT_Allocator_ID);
      _BAT_Util_renew_alloc(obj->_allocators, id, 0);
    }
  }
  obj->_state = _BAT_State_zero();
  return obj;
}

BAT_Base *
_BAT_Base_copy(BAT_Base * o, size_t sz, BAT_Allocator_ID child_id)
{
  BAT_Base *copy;
  da_pos_t i, len;

  copy = malloc(sz);
  copy->_state = o->_state;
  copy->_parent = NULL;

  copy->_children = NULL;
  copy->_allocators = NULL;

  if (o->_allocators != NULL) {
    copy->_allocators = keyed_set_copy(o->_allocators);
    copy->_children = keyed_set_get(copy->_allocators, child_id);
  }
  else if (o->_children != NULL) {
    copy->_children = darray_copy(o->_children);
  }

  if (copy->_children != NULL) {
    len = darray_size(copy->_children);
    for (i = 0; i < len; i++)
      ((BAT_Base *) darray_get(copy->_children, i))->_parent = copy;
  }

  return copy;
}

void
_BAT_Base_delete(BAT_Base * o, int befrei)
{
  if (o->_allocators != NULL)
    keyed_set_delete(o->_allocators, 1);
  else if (o->_children != NULL)
    darray_delete(o->_children, 1);

  if (befrei)
    free(o);
}

void
_BAT_Base_set_parent(BAT_Base * child, BAT_Base * parent)
{
  child->_parent = parent;
}

void
_BAT_Base_update_state(BAT_Base * o, BAT_State state)
{
  o->_state = _BAT_State_union(o->_state, state);
  _BAT_Base_tell_parents(o);
}

void
_BAT_Base_tell_parents(BAT_Base * o)
{
  BAT_Base *cur = o;
  while (cur->_parent != NULL) {
    cur->_parent->_state = _BAT_State_union(cur->_parent->_state, cur->_state);
    cur = cur->_parent;
  }
}

void
_BAT_Base_tell_children(BAT_Base * o, BAT_State state)
{
  da_pos_t i, sz;
  BAT_Base *cur;

  sz = darray_size(o->_children);
  for (i = 0; i < sz; i++) {
    cur = (BAT_Base *) darray_get(o->_children, i);
    cur->_state = _BAT_State_union(cur->_state, state);
    if (cur->_children != NULL)
      _BAT_Base_tell_children(cur, state);
  }
}

/*
 * --- BAT_GeneTab ---
 */

BAT_GeneTab *
BAT_GeneTab_new()
{
  return (BAT_GeneTab *) _BAT_Base_init(sizeof(BAT_GeneTab), 3, Gene_Allocator,
					Transcript_Allocator,
					Feature_Allocator);

}

BAT_GeneTab *
BAT_GeneTab_copy(BAT_GeneTab * gt)
{
  return (BAT_GeneTab *) _BAT_Base_copy((BAT_Base *) gt, sizeof(BAT_GeneTab),
					Gene_Allocator);
}

void
BAT_GeneTab_delete(BAT_GeneTab * gt, int befrei)
{
  _BAT_Base_delete((BAT_Base *) gt, 0);

  if (befrei)
    free(gt);
}

void
BAT_GeneTab_insert_gene(BAT_GeneTab * gt, BAT_Gene * gene)
{
  BAT_State delta = _BAT_State_zero();

  delta.mod_genes |= 1;

  _BAT_Base_update_state((BAT_Base *) gt, delta);
  darray_place(gt->_children, gene, BAT_Gene_order);
}

/* Possibly could define masks to save space on these lengthy state changes */
void
BAT_GeneTab_add_gene(BAT_GeneTab * gt, BAT_Gene * gene)
{
  BAT_State delta = _BAT_State_zero();

  delta.mod_genes |= 1;
  delta.add_genes |= 1;

  _BAT_Base_update_state((BAT_Base *) gt, delta);
  darray_add(gt->_children, gene);
  _BAT_Base_set_parent((BAT_Base *) gene, (BAT_Base *) gt);
}

BAT_Gene *
BAT_GeneTab_remove_gene(BAT_GeneTab * gt, BAT_Gene * gene, int befrei)
{
  BAT_State delta = _BAT_State_zero();

  delta.mod_genes |= 1;

  _BAT_Base_update_state((BAT_Base *) gt, delta);
  /* unimplemented -- is this needed? */
  /*_BAT_GeneTab_update_minmax(gt, NULL);*/

  /*BAT_Gene_delete(gene, 1); */
  return (BAT_Gene *) darray_remove(gt->_children,
				    darray_pos(gt->_children, gene), befrei);
}

BAT_Gene **
BAT_GeneTab_get_genes(BAT_GeneTab * gt, da_pos_t *n_genes)
{
  _BAT_GeneTab_update(gt);
  *n_genes = darray_size(gt->_children);
  return (BAT_Gene **) darray_get_array(gt->_children);
}

BAT_Gene *
BAT_GeneTab_find_gene(BAT_GeneTab * gt, const char *id)
{
  /* FIXME need a good implementation of this -- hash would be better */
  /*
   * return (BAT_Gene *)darray_find(gt->_children, id,
   * BAT_Gene_id_cmp);
   */
  return NULL;
}

BAT_Transcript **
BAT_GeneTab_get_transcripts(BAT_GeneTab * gt, da_pos_t *n_txs)
{
  _BAT_GeneTab_update(gt);
  *n_txs = darray_size(keyed_set_get(gt->_allocators, Transcript_Allocator));
  return (BAT_Transcript **)
    darray_get_array(keyed_set_get(gt->_allocators, Transcript_Allocator));
}

BAT_Transcript *
BAT_GeneTab_find_transcript(BAT_GeneTab * gt, const char *id)
{
  da_pos_t i, j, sz1, sz2;
  darray_t *genealloc, *txalloc;
  BAT_Gene *gene;
  BAT_Transcript *tx;

  genealloc = gt->_children;
  sz1 = darray_size(genealloc);
  for (i = 0; i < sz1; i++) {	/* gene */
    gene = (BAT_Gene *) darray_get(genealloc, i);
    txalloc = gene->_children;
    sz2 = darray_size(txalloc);
    for (j = 0; j < sz2; j++) {	/* tx */
      tx = (BAT_Transcript *) darray_get(txalloc, j);
      if (!strcmp(tx->id, id))
	return tx;
    }
  }
  return NULL;
}

BAT_Feature **
BAT_GeneTab_get_features(BAT_GeneTab * gt, da_pos_t *n_feats)
{
  _BAT_GeneTab_update(gt);
  *n_feats = darray_size(keyed_set_get(gt->_allocators, Feature_Allocator));

  return (BAT_Feature **)
    darray_get_array(keyed_set_get(gt->_allocators, Feature_Allocator));
}

void
_BAT_GeneTab_update_genes(BAT_GeneTab * gt)
{
  darray_sort(gt->_children, BAT_Gene_order);
}

void
_BAT_GeneTab_update_transcripts(BAT_GeneTab * gt)
{
  darray_t *srcalloc, *dstalloc, *txalloc;
  da_pos_t i, j, gsz, tsz;
  BAT_Gene *gene;

  _BAT_Util_renew_alloc(gt->_allocators, Transcript_Allocator, 0);
  srcalloc = gt->_children;
  dstalloc = keyed_set_get(gt->_allocators, Transcript_Allocator);
  gsz = darray_size(srcalloc);
  for (i = 0; i < gsz; i++) {	/* gene */
    gene = (BAT_Gene *) darray_get(srcalloc, i);
    txalloc = keyed_set_get(gene->_allocators, Transcript_Allocator);
    tsz = darray_size(txalloc);
    for (j = 0; j < tsz; j++)	/* tx */
      darray_add(dstalloc, darray_get(txalloc, j));
  }

  darray_sort(dstalloc, BAT_Transcript_order);
}

void
_BAT_GeneTab_update_features(BAT_GeneTab * gt)
{
  da_pos_t i, j, k, gsz, tsz, fsz;
  BAT_Gene *gene;
  BAT_Transcript *tx;
  darray_t *srcalloc, *dstalloc, *txalloc, *featalloc;

  _BAT_Util_renew_alloc(gt->_allocators, Feature_Allocator, 0);
  srcalloc = gt->_children;
  dstalloc = keyed_set_get(gt->_allocators, Feature_Allocator);
  gsz = darray_size(srcalloc);
  for (i = 0; i < gsz; i++) {	/* gene */
    gene = (BAT_Gene *) darray_get(srcalloc, i);
    txalloc = keyed_set_get(gene->_allocators, Transcript_Allocator);
    tsz = darray_size(txalloc);
    for (j = 0; j < tsz; j++) {	/* tx */
      tx = (BAT_Transcript *) darray_get(txalloc, j);
      featalloc = tx->_children;
      fsz = darray_size(featalloc);
      for (k = 0; k < fsz; k++)	/* feature */
	darray_add(dstalloc, darray_get(featalloc, k));
    }
  }

  darray_sort(dstalloc, BAT_Feature_order);
}

void
_BAT_GeneTab_update(BAT_GeneTab * gt)
{
  if (gt->_state.mod_features || gt->_state.add_features) {
    _BAT_GeneTab_update_features(gt);
  }
  else if (gt->_state.mod_transcripts || gt->_state.add_transcripts) {
    _BAT_GeneTab_update_transcripts(gt);
    _BAT_GeneTab_update_features(gt);
  }
  else if (gt->_state.add_genes) {
    _BAT_GeneTab_update_genes(gt);
    _BAT_GeneTab_update_transcripts(gt);
    _BAT_GeneTab_update_features(gt);
  }
  gt->_state = _BAT_State_zero();
}

/*
 * --- BAT_Gene ---
 */

BAT_Gene *
BAT_Gene_new()
{
  BAT_Gene *gene;

  gene =
    (BAT_Gene *) _BAT_Base_init(sizeof(BAT_Gene), 2, Transcript_Allocator,
				Feature_Allocator);

  gene->seqname = NULL;
  gene->source = NULL;
  gene->id = NULL;

  gene->strand = STRAND;

  gene->min = 0;
  gene->max = 0;

  return gene;
}

BAT_Gene *
BAT_Gene_copy(BAT_Gene * gene)
{
  BAT_Gene *copy;

  copy =
    (BAT_Gene *) _BAT_Base_copy((BAT_Base *) gene, sizeof(BAT_Gene),
				Transcript_Allocator);

  copy->seqname = BAT_Util_copy_string(gene->seqname);
  copy->source = BAT_Util_copy_string(gene->source);
  copy->id = BAT_Util_copy_string(gene->id);
  copy->strand = gene->strand;

  return copy;
}

void
BAT_Gene_delete(BAT_Gene * gene, int befrei)
{
  _BAT_Base_delete((BAT_Base *) gene, 0);

  if (gene->seqname != NULL)
    free(gene->seqname);
  if (gene->source != NULL)
    free(gene->source);
  if (gene->id != NULL)
    free(gene->id);
  gene->strand = STRAND;
  if (befrei)
    free(gene);
}

void
BAT_Gene_set_all(BAT_Gene * gene, const char *seqname, const char *source,
		 BAT_Strand strand, const char *id)
{
  BAT_Gene_set_seqname(gene, seqname);
  BAT_Gene_set_source(gene, source);
  BAT_Gene_set_strand(gene, strand);
  BAT_Gene_set_id(gene, id);
}

void
BAT_Gene_set_id(BAT_Gene * gene, const char *id)
{
  BAT_State delta = _BAT_State_zero();

  delta.mod_gene_id |= 1;

  _BAT_Base_update_state((BAT_Base *) gene, delta);
  /*_BAT_Base_tell_children((BAT_Base *) gene, delta);*/
  gene->id = BAT_Util_copy_string(id);
}

void
BAT_Gene_set_seqname(BAT_Gene * gene, const char *seqname)
{
  gene->seqname = BAT_Util_copy_string(seqname);
}

void
BAT_Gene_set_source(BAT_Gene * gene, const char *source)
{
  gene->source = BAT_Util_copy_string(source);
}

void
BAT_Gene_set_strand(BAT_Gene * gene, BAT_Strand strand)
{
  gene->strand = strand;
}

BAT_Coord
BAT_Gene_get_min(BAT_Gene * gene)
{
  _BAT_Gene_update(gene);
  return gene->min;
}

BAT_Coord
BAT_Gene_get_max(BAT_Gene * gene)
{
  _BAT_Gene_update(gene);
  return gene->max;
}

void
_BAT_Gene_update_minmax(BAT_Gene * gene, BAT_Transcript * tx)
{
  BAT_Transcript *ltx;
  da_pos_t i, sz;

  if (tx == NULL) {
    sz = darray_size(gene->_children);
    for (i = 0; i < sz; i++) {
      ltx = darray_get(gene->_children, i);
      if (ltx->min < gene->min || gene->min == 0)
	gene->min = ltx->min;
      if (ltx->max > gene->max || gene->max == 0)
	gene->max = ltx->max;
    }
  }
  else {
    if (tx->min < gene->min || gene->min == 0)
      gene->min = tx->min;
    if (tx->max > gene->max || gene->max == 0)
      gene->max = tx->max;
  }
}

void
BAT_Gene_insert_transcript(BAT_Gene * gene, BAT_Transcript * tx)
{
  BAT_State delta = _BAT_State_zero();

  delta.mod_transcripts |= 1;

  _BAT_Gene_update(gene);
  _BAT_Base_update_state((BAT_Base *) gene, delta);
  darray_place(gene->_children, tx, BAT_Transcript_order);
  _BAT_Base_set_parent((BAT_Base *) tx, (BAT_Base *) gene);
  /*
   * darray_get (gene->_children, darray_place(gene->_children, tx,
   * BAT_Transcript_order)),
   */
  _BAT_Gene_update_minmax(gene, tx);
}

void
BAT_Gene_add_transcript(BAT_Gene * gene, BAT_Transcript * tx)
{
  BAT_State delta = _BAT_State_zero();

  delta.mod_transcripts |= 1;
  delta.add_transcripts |= 1;

  _BAT_Base_update_state((BAT_Base *) gene, delta);
  darray_add(gene->_children, tx);
  _BAT_Base_set_parent((BAT_Base *) tx, (BAT_Base *) gene);
  _BAT_Gene_update_minmax(gene, tx);
}

BAT_Transcript *
BAT_Gene_remove_transcript(BAT_Gene * gene, BAT_Transcript * tx, int befrei)
{
  BAT_State delta = _BAT_State_zero();

  delta.mod_transcripts |= 1;

  _BAT_Base_update_state((BAT_Base *) gene, delta);
  _BAT_Gene_update_minmax(gene, NULL);
  return (BAT_Transcript *) darray_remove(gene->_children,
					  darray_pos(gene->_children, tx),
					  befrei);
}

BAT_Transcript **
BAT_Gene_get_transcripts(BAT_Gene * gene, da_pos_t *n_txs)
{
  _BAT_Gene_update(gene);
  *n_txs = darray_size(gene->_children);
  return (BAT_Transcript **) darray_get_array(gene->_children);
}

BAT_Feature **
BAT_Gene_get_features(BAT_Gene * gene, da_pos_t *n_feats)
{
  _BAT_Gene_update(gene);
  *n_feats = darray_size(keyed_set_get(gene->_allocators, Feature_Allocator));
  return (BAT_Feature **)
    darray_get_array(keyed_set_get(gene->_allocators, Feature_Allocator));
}

int
BAT_Gene_order(const void **p1, const void **p2)
{
  BAT_Gene *g1 = *((BAT_Gene **) p1), *g2 = *((BAT_Gene **) p2);
  if (g1->min < g2->min)
    return -1;
  if (g1->min > g2->min)
    return 1;
  if (g1->max > g2->max)
    return -1;
  if (g1->max < g2->max)
    return 1;
  return 0;
}

int
BAT_Gene_cmp(const BAT_Gene * p1, const BAT_Gene * p2)
{
  /* WRITEME: compare them to find how equal (overlapping) they are */
  return 0;
}

int
BAT_Gene_id_cmp(const void *p1, const void *p2)
{
  return strcmp(((BAT_Gene *) p1)->id, (((BAT_Gene *) p2)->id));
}

void
_BAT_Gene_update_transcripts(BAT_Gene * gene)
{
  da_pos_t i, tsz;
  darray_t *srcalloc;
  BAT_Transcript *tx;

  srcalloc = gene->_children;
  tsz = darray_size(srcalloc);
  for (i = 0; i < tsz; i++) {
    tx = darray_get(srcalloc, i);
    if (tx->min < gene->min || !gene->min)
      gene->min = tx->min;
    if (tx->max > gene->max || !gene->max)
      gene->max = tx->max;
  }
  darray_sort(gene->_children, BAT_Transcript_order);
}

void
_BAT_Gene_update_features(BAT_Gene * gene)
{
  da_pos_t i, j, tsz, fsz;
  darray_t *srcalloc, *dstalloc, *featalloc;
  BAT_Transcript *tx;

  _BAT_Util_renew_alloc(gene->_allocators, Feature_Allocator, 0);
  srcalloc = gene->_children;
  dstalloc = keyed_set_get(gene->_allocators, Feature_Allocator);
  tsz = darray_size(srcalloc);
  for (i = 0; i < tsz; i++) {	/* tx */
    tx = (BAT_Transcript *) darray_get(srcalloc, i);
    featalloc = tx->_children;
    fsz = darray_size(featalloc);
    for (j = 0; j < fsz; j++)	/* feat */
      darray_add(dstalloc, darray_get(featalloc, j));
  }
  darray_sort(dstalloc, BAT_Feature_order);
}

void
_BAT_Gene_update(BAT_Gene * gene)
{
  if (gene->_state.mod_features || gene->_state.add_features) {
    _BAT_Gene_update_features(gene);
  }
  if (gene->_state.add_transcripts || gene->_state.mod_transcripts) {
    _BAT_Gene_update_transcripts(gene);
    _BAT_Gene_update_features(gene);
  }
  /*
   * XXX: not updating the min and max, but we're not sure if we need
   * to
   */
}

/*
 * --- BAT_Transcript ---
 */

BAT_Transcript *
BAT_Transcript_new()
{
  BAT_Transcript *tx;
  tx =
    (BAT_Transcript *) _BAT_Base_init(sizeof(BAT_Transcript), 1,
				      Feature_Allocator);

  tx->id = malloc(sizeof(char));
  *tx->id = '\0';
  tx->min = 0;
  tx->max = 0;

  return tx;
}

BAT_Transcript *
BAT_Transcript_copy(BAT_Transcript * tx)
{
  BAT_Transcript *copy;

  copy =
    (BAT_Transcript *) _BAT_Base_copy((BAT_Base *) tx, sizeof(BAT_Transcript),
				      Feature_Allocator);

  copy->id = BAT_Util_copy_string(tx->id);
  copy->min = tx->min;
  copy->max = tx->max;

  return copy;
}

void
BAT_Transcript_delete(BAT_Transcript * tx, int befrei)
{
  _BAT_Base_delete((BAT_Base *) tx, 0);

  if (tx->id != NULL)
    free(tx->id);
  if (befrei)
    free(tx);
}

void
_BAT_Transcript_update_minmax(BAT_Transcript * tx, BAT_Feature * feat)
{
  BAT_Feature *lfeat;
  da_pos_t i, sz;
  if (feat == NULL) {
    sz = darray_size(tx->_children);
    for (i = 0; i < sz; i++) {
      lfeat = darray_get(tx->_children, i);
      if (lfeat->start < tx->min || tx->min == 0)
	tx->min = lfeat->start;
      if (lfeat->end > tx->max || tx->max == 0)
	tx->max = lfeat->end;
    }
  }
  else {
    if (feat->start < tx->min || tx->min == 0)
      tx->min = feat->start;
    if (feat->end > tx->max || tx->max == 0)
      tx->max = feat->end;
  }
}

void
BAT_Transcript_insert_feature(BAT_Transcript * tx, BAT_Feature * feat)
{
  BAT_State delta = _BAT_State_zero();

  delta.mod_features |= 1;

  _BAT_Base_update_state((BAT_Base *) tx, delta);
  darray_place(tx->_children, tx, BAT_Transcript_order);
  _BAT_Base_set_parent((BAT_Base *) feat, (BAT_Base *) tx);
  /*
   * darray_get (feat->attributes, darray_place(tx->_children, tx,
   * BAT_Transcript_order))
   */
  _BAT_Transcript_update_minmax(tx, feat);
}

void
BAT_Transcript_add_feature(BAT_Transcript * tx, BAT_Feature * feat)
{
  BAT_State delta = _BAT_State_zero();

  delta.mod_features |= 1;
  delta.add_features |= 1;

  _BAT_Base_update_state((BAT_Base *) tx, delta);
  darray_add(tx->_children, feat);
  _BAT_Base_set_parent((BAT_Base *) feat, (BAT_Base *) tx);
  _BAT_Transcript_update_minmax(tx, feat);
}

BAT_Feature *
BAT_Transcript_remove_feature(BAT_Transcript * tx, BAT_Feature * feat,
			      int befrei)
{
  BAT_State delta = _BAT_State_zero();

  delta.mod_features |= 1;

  _BAT_Base_update_state((BAT_Base *) tx, delta);
  _BAT_Transcript_update_minmax(tx, NULL);
  return (BAT_Feature *) darray_remove(tx->_children,
				       darray_pos(tx->_children, feat), befrei);
}

BAT_Feature **
BAT_Transcript_get_features(BAT_Transcript * tx, da_pos_t *n_feats)
{
  _BAT_Transcript_update(tx);
  *n_feats = darray_size(tx->_children);
  return (BAT_Feature **) darray_get_array(tx->_children);
}

int
BAT_Transcript_order(const void **p1, const void **p2)
{
  BAT_Transcript *t1 = *((BAT_Transcript **) p1), *t2 =
    *((BAT_Transcript **) p2);
  int cmp = t1->min - t2->min;
  if (cmp != 0)
    return cmp;
  cmp = t1->max - t2->max;
  return cmp;
}

int
BAT_Transcript_cmp(const BAT_Transcript * t1, const BAT_Transcript * t2)
{
  /* WRITEME: how much do they overlap ? */
  return 0;
}

int
BAT_Transcript_id_cmp(const void *p1, const void *p2)
{
  return strcmp(((BAT_Transcript *) p1)->id, (((BAT_Transcript *) p2)->id));
}

void
BAT_Transcript_set_id(BAT_Transcript * tx, const char *id)
{
  BAT_State delta = _BAT_State_zero();

  delta.mod_transcript_id |= 1;

  _BAT_Base_update_state((BAT_Base *) tx, delta);
  /*_BAT_Base_tell_children((BAT_Base *) tx, delta);*/
  if (tx->id != NULL)
    free(tx->id);
  tx->id = BAT_Util_copy_string(id);
}

char *
BAT_Transcript_get_gene_id(BAT_Transcript * tx)
{
  if (tx->_parent != NULL)
    return tx->_parent->id;
  else
    return NULL;
}

BAT_Coord
BAT_Transcript_get_min(BAT_Transcript * tx)
{
  _BAT_Transcript_update(tx);
  return tx->min;
}

BAT_Coord
BAT_Transcript_get_max(BAT_Transcript * tx)
{
  _BAT_Transcript_update(tx);
  return tx->max;
}

void
_BAT_Transcript_update(BAT_Transcript * tx)
{
  /*
   * int i, fsz; BAT_Feature *feat;
   */
  darray_t *feat_alloc;

  feat_alloc = tx->_children;
  if (tx->_state.mod_features || tx->_state.add_features) {
    darray_sort(feat_alloc, BAT_Feature_order);
  }
  if (tx->_state.add_features) {
    /* XXX: is this necessary or not? */
    /*
     * fsz = darray_size(feat_alloc);
     * for (i = 0; i < fsz; i++) {
     * feat = (BAT_Feature *) darray_get(feat_alloc, i);
     * if (feat->start < tx->min || !tx->min)
     * tx->min = feat->start;
     * if (feat->end > tx->max || !tx->max)
     * tx->max = feat->end;
     * }
     */
  }
}

BAT_Feature_Type
BAT_Feature_Type_from_str(const char *str)
{
  int i;
  for (i = 0; i < FEATURE_TYPE; i++)
    if (!strcmp(_BAT_Feature_Names[i], str))
      return i;
  return FEATURE_TYPE;
}

char *
BAT_Feature_Type_get_str(BAT_Feature_Type ft)
{
  return _BAT_Feature_Names[ft];
}

char
BAT_Strand_get_strand_char(BAT_Strand s)
{
  return _BAT_Strands[s];
}

/*
 * --- BAT_Feature_Base ---
 */

BAT_Feature *
_BAT_Feature_Base_init(size_t size)
{
  BAT_Feature *feat;

  feat = malloc(size);

  feat->attributes = NULL;
  feat->attributes =
    _BAT_Util_direct_renew_alloc(feat->attributes, Attribute_Allocator, 1);

  feat->_parent = NULL;
  feat->type = FEATURE_TYPE;
  feat->start = 0;
  feat->end = 0;
  feat->score = 0.;
  feat->comment = BAT_Comment_new();

  return feat;
}

BAT_Feature *
_BAT_Feature_Base_copy(BAT_Feature * feat, size_t size)
{
  BAT_Feature *copy;

  copy = malloc(size);

  copy->_parent = NULL;
  copy->attributes = darray_copy(feat->attributes);
  copy->type = feat->type;
  copy->start = feat->start;
  copy->end = feat->end;
  copy->score = feat->score;
  copy->frame = feat->frame;

  copy->comment = BAT_Comment_copy(feat->comment);

  return copy;
}

void
_BAT_Feature_Base_delete(BAT_Feature * feat, int befrei)
{
  if (feat->attributes != NULL)
    darray_delete(feat->attributes, 1);

  if (feat->comment != NULL)
    BAT_Comment_delete(feat->comment, 1);

  if (befrei)
    free(feat);
}

void
_BAT_Feature_Base_set_all(BAT_Feature * feat, BAT_Feature_Type type,
			  BAT_Coord start, BAT_Coord end, int score, int frame)
{
  /* XXX a little laziness here again, this should be in the base class. */
  BAT_Feature_set_type(feat, type);
  BAT_Feature_set_start(feat, start);
  BAT_Feature_set_end(feat, end);
  BAT_Feature_set_score(feat, score);
  BAT_Feature_set_frame(feat, frame);
}

int
_BAT_Feature_Base_order(const void **p1, const void **p2)
{
  BAT_Feature *f1 = *((BAT_Feature **) p1), *f2 = *((BAT_Feature **) p2);
  int cmp = f1->start - f2->start;
  if (cmp != 0)
    return cmp;
  cmp = f1->end - f2->end;
  return cmp;
}

/*
 * this could be a potential problem.  could we be copying out the same
 * comment
 */
void
_BAT_Feature_Base_set_comment(BAT_Feature * feat, BAT_Comment * cmt)
{
  if (feat->comment != NULL)
    BAT_Comment_delete(feat->comment, 1);
  feat->comment = cmt;
}


void
_BAT_Feature_Base_set_transcript_id(BAT_Feature * feat,
				    const char *transcript_id)
{
  _BAT_Feature_Base_set_attribute_by_keyvalue(feat, "transcript_id",
					      transcript_id);
}

void
_BAT_Feature_Base_set_gene_id(BAT_Feature * feat, const char *gene_id)
{
  _BAT_Feature_Base_set_attribute_by_keyvalue(feat, "gene_id", gene_id);
}

char
_BAT_Feature_Base_get_frame_char(BAT_Feature * feat)
{
  switch (feat->frame) {
  case 0:
  case 1:
  case 2:
    return '0' + feat->frame;
  }
  return '?';
}

void
_BAT_Feature_Base_set_attribute_unchecked(BAT_Feature * feat,
					  BAT_Attribute * attr)
{
  BAT_Attribute *curattr;
  /*BAT_State delta; */

  curattr = BAT_Feature_find_attribute(feat, attr->key);
  if (curattr != NULL)
    BAT_Feature_remove_attribute(feat, curattr, 1);

  /* elminiated code because it's currently meaningless to tell anyone we have
   * changed our attributes */
  /*
   * delta = _GTF_State_zero();
   * delta.mod_attributes |= 1;
   * _BAT_Base_update_state((BAT_Base *) feat, delta);
   */

  darray_place(feat->attributes, attr, BAT_Attribute_order);
}

void
_BAT_Feature_Base_set_attribute_by_keyvalue(BAT_Feature * feat, const char *key,
					    const char *val)
{
  BAT_Attribute *attr;
  attr = BAT_Attribute_new();
  BAT_Attribute_set_all(attr, key, val);
  _BAT_Feature_Base_set_attribute_unchecked(feat, attr);
}

BAT_Attribute *
_BAT_Feature_Base_remove_attribute(BAT_Feature * feat, BAT_Attribute * attr,
				   int befrei)
{
  /*BAT_Attribute_delete(attr); */
  return (BAT_Attribute *) darray_remove(feat->attributes,
					 darray_pos(feat->attributes, attr),
					 befrei);
}

BAT_Attribute **
_BAT_Feature_Base_get_attributes(BAT_Feature * feat, da_pos_t *sz)
{
  *sz = darray_size(feat->attributes);
  return (BAT_Attribute **) darray_get_array(feat->attributes);
}

/*
 * --- BAT_Feature ---
 */

BAT_Feature *
BAT_Feature_new()
{
  return (BAT_Feature *) _BAT_Feature_Base_init(sizeof(BAT_Feature));
}

BAT_Feature *
BAT_Feature_copy(BAT_Feature * feat)
{
  return _BAT_Feature_Base_copy(feat, sizeof(BAT_Feature));
}

void
BAT_Feature_delete(BAT_Feature * feat, int befrei)
{
  _BAT_Feature_Base_delete(feat, 0);

  if (befrei)
    free(feat);
}

void
BAT_Feature_set_all(BAT_Feature * feat, BAT_Feature_Type type, BAT_Coord start,
		    BAT_Coord end, int score, int frame)
{
  _BAT_Feature_Base_set_all(feat, type, start, end, score, frame);
}

int
BAT_Feature_order(const void **p1, const void **p2)
{
  return _BAT_Feature_Base_order(p1, p2);
}

int
BAT_Feature_cmp(const BAT_Feature * f1, const BAT_Feature * f2)
{
  /* FIXME: stopgap comparison, only says if they're equal or not */

  if (f1->type == f2->type
      && BAT_Feature_get_strand(f1) == BAT_Feature_get_strand(f2)
      && f1->start == f2->start && f1->end == f2->end)
    return 0;
  return 1;
}

/*
 * this could be a potential problem.  could we be copying out the same
 * comment
 */
void
BAT_Feature_set_comment(BAT_Feature * feat, BAT_Comment * cmt)
{
  _BAT_Feature_Base_set_comment(feat, cmt);
}

void
BAT_Feature_set_frame(BAT_Feature * feat, int frame)
{
  feat->frame = frame;
}

void
BAT_Feature_set_feature_type(BAT_Feature * feat, BAT_Feature_Type type)
{
  feat->type = type;
}

void
BAT_Feature_set_type(BAT_Feature * feat, BAT_Feature_Type type)
{
  feat->type = type;
}

void
BAT_Feature_set_start(BAT_Feature * feat, BAT_Coord start)
{
  BAT_State delta;

  feat->start = start;

  if (feat->_parent != NULL) {
    delta = _BAT_State_zero();
    delta.mod_features |= 1;
    _BAT_Base_update_state((BAT_Base *) feat->_parent, delta);
  }
}

void
BAT_Feature_set_end(BAT_Feature * feat, BAT_Coord end)
{
  BAT_State delta;

  feat->end = end;

  if (feat->_parent != NULL) {
    delta = _BAT_State_zero();
    delta.mod_features |= 1;
    _BAT_Base_update_state((BAT_Base *) feat, delta);
  }
}

void
BAT_Feature_set_score(BAT_Feature * feat, int score)
{
  feat->score = score;
}

void
BAT_Feature_set_attribute(BAT_Feature * feat, BAT_Attribute * attr)
{
  /*
   * if we already are a part of a gene or a transcript, we don't want
   * to unilatterally alter the id.
   */
  if (!strcmp(attr->key, "gene_id") || !strcmp(attr->key, "transcript_id")) {
    BAT_Attribute_delete(attr, 1);
    return;
  }

  _BAT_Feature_Base_set_attribute_unchecked(feat, attr);
}

void
BAT_Feature_set_attribute_by_keyvalue(BAT_Feature * feat, const char *key,
				      const char *val)
{
  if (strcmp(key, "gene_id") || strcmp(key, "transcript_id"))
    return;			/* die error msg? */
  _BAT_Feature_Base_set_attribute_by_keyvalue(feat, key, val);
}

BAT_Attribute *
BAT_Feature_remove_attribute(BAT_Feature * feat, BAT_Attribute * attr,
			     int befrei)
{
  return _BAT_Feature_Base_remove_attribute(feat, attr, befrei);
}

char *
BAT_Feature_get_gene_id(BAT_Feature * feat)
{
  if (feat->_parent != NULL && feat->_parent->_parent != NULL) {
    return feat->_parent->_parent->id;
  }
  else {
    fprintf(stderr,
	    "BAT_Feature: Attempt to access gene_id of orphaned feature.\n");
    exit(1);
  }
}

char *
BAT_Feature_get_transcript_id(BAT_Feature * feat)
{
  if (feat->_parent != NULL)
    return feat->_parent->id;
  else {
    fprintf(stderr,
	    "BAT_Feature: Attempt to access transcript_id of orphaned feature.\n");
    exit(1);
  }
}


BAT_Strand
BAT_Feature_get_strand(const BAT_Feature * feat)
{
  if (feat->_parent != NULL && feat->_parent->_parent != NULL)
    return feat->_parent->_parent->strand;
  else {
    fprintf(stderr,
	    "BAT_Feature: Attempt to access strand of orphaned feature.\n");
    exit(1);
  }

}
char *
BAT_Feature_get_seqname(BAT_Feature * feat)
{
  if (feat->_parent != NULL && feat->_parent->_parent != NULL)
    return feat->_parent->_parent->seqname;
  else {
    fprintf(stderr,
	    "BAT_Feature: Attempt to access seqname of orphaned feature.\n");
    exit(1);
  }
}
char *
BAT_Feature_get_source(BAT_Feature * feat)
{
  if (feat->_parent != NULL && feat->_parent->_parent != NULL) {
    return feat->_parent->_parent->source;
  }
  else {
    fprintf(stderr,
	    "BAT_Feature: Attempt to access source of orphaned feature.\n");
    exit(1);
  }
}

int
BAT_Feature_gid_cmp(const void **f1, const void **f2)
{
  return strcmp(BAT_Feature_get_gene_id(*((BAT_Feature **) f1)),
		BAT_Feature_get_gene_id(*((BAT_Feature **) f2)));
}

int
BAT_Feature_tid_cmp(const void **f1, const void **f2)
{
  return strcmp(BAT_Feature_get_transcript_id(*((BAT_Feature **) f1)),
		BAT_Feature_get_transcript_id(*((BAT_Feature **) f2)));
}

BAT_Attribute **
BAT_Feature_get_attributes(BAT_Feature * feat, da_pos_t *sz)
{
  return _BAT_Feature_Base_get_attributes(feat, sz);
}

char
BAT_Feature_get_frame_char(BAT_Feature * feat)
{
  return _BAT_Feature_Base_get_frame_char(feat);
}

BAT_Attribute *
BAT_Feature_find_attribute(BAT_Feature * feat, const char *key)
{
  return darray_find(feat->attributes, key, BAT_Attribute_key_cmp);
}

/*
 * --- BAT_Free_Feature ---
 */

BAT_Free_Feature *
BAT_Free_Feature_new()
{
  BAT_Free_Feature *feat;

  feat = (BAT_Free_Feature *) _BAT_Feature_Base_init(sizeof(BAT_Free_Feature));

  feat->strand = STRAND;
  feat->seqname = NULL;
  feat->source = NULL;

  return feat;
}

BAT_Free_Feature *
BAT_Free_Feature_copy(BAT_Free_Feature * feat)
{
  BAT_Free_Feature *copy;

  copy =
    (BAT_Free_Feature *) _BAT_Feature_Base_copy((BAT_Feature *) feat,
						sizeof(BAT_Free_Feature));

  copy->strand = feat->strand;
  copy->seqname = BAT_Util_copy_string(feat->seqname);
  copy->source = BAT_Util_copy_string(feat->source);

  return copy;
}

void
BAT_Free_Feature_delete(BAT_Free_Feature * feat, int befrei)
{
  _BAT_Feature_Base_delete((BAT_Feature *) feat, 0);

  if (feat->source != NULL)
    free(feat->source);

  if (feat->seqname != NULL)
    free(feat->seqname);

  if (befrei)
    free(feat);
}

void
BAT_Free_Feature_set_all(BAT_Free_Feature * feat, BAT_Feature_Type type,
			 BAT_Coord start, BAT_Coord end, int score, int frame,
			 BAT_Strand strand, const char *seqname,
			 const char *source)
{
  _BAT_Feature_Base_set_all((BAT_Feature *) feat, type, start, end, score,
			    frame);
  BAT_Free_Feature_set_strand(feat, strand);
  BAT_Free_Feature_set_seqname(feat, seqname);
  BAT_Free_Feature_set_source(feat, source);
}

int
BAT_Free_Feature_order(const void **p1, const void **p2)
{
  return _BAT_Feature_Base_order(p1, p2);
}

int
BAT_Free_Feature_cmp(const BAT_Free_Feature * f1, const BAT_Free_Feature * f2)
{
  /* FIXME: stopgap comparison, only says if they're equal or not */
  if (f1->type == f2->type && f1->strand == f2->strand && f1->start == f2->start
      && f1->end == f2->end)
    return 0;
  return 1;
}

/*
 * this could be a potential problem.  could we be copying out the same
 * comment
 */
void
BAT_Free_Feature_set_comment(BAT_Free_Feature * feat, BAT_Comment * cmt)
{
  _BAT_Feature_Base_set_comment((BAT_Feature *) feat, cmt);
}

void
BAT_Free_Feature_set_seqname(BAT_Free_Feature * feat, const char *seqname)
{
  char *cpy = BAT_Util_copy_string(seqname);
  if (feat->seqname != NULL)
    free(feat->seqname);
  feat->seqname = cpy;
}

void
BAT_Free_Feature_set_source(BAT_Free_Feature * feat, const char *source)
{
  char *cpy = BAT_Util_copy_string(source);
  if (feat->source != NULL)
    free(feat->source);
  feat->source = cpy;
}

void
BAT_Free_Feature_set_transcript_id(BAT_Free_Feature * feat,
				   const char *transcript_id)
{
  BAT_Free_Feature_set_attribute_by_keyvalue(feat, "transcript_id",
					     transcript_id);
}

void
BAT_Free_Feature_set_gene_id(BAT_Free_Feature * feat, const char *gene_id)
{
  BAT_Free_Feature_set_attribute_by_keyvalue(feat, "gene_id", gene_id);
}

void
BAT_Free_Feature_set_frame(BAT_Free_Feature * feat, int frame)
{
  feat->frame = frame;
}

void
BAT_Free_Feature_set_feature_type(BAT_Free_Feature * feat,
				  BAT_Feature_Type type)
{
  feat->type = type;
}

void
BAT_Free_Feature_set_strand(BAT_Free_Feature * feat, BAT_Strand strand)
{
  feat->strand = strand;
}

void
BAT_Free_Feature_set_start(BAT_Free_Feature * feat, BAT_Coord start)
{
  feat->start = start;
}

void
BAT_Free_Feature_set_end(BAT_Free_Feature * feat, BAT_Coord end)
{
  feat->end = end;
}

void
BAT_Free_Feature_set_score(BAT_Free_Feature * feat, int score)
{
  feat->score = score;
}

/* XXX: consider renaming this? */
void
BAT_Free_Feature_set_attribute(BAT_Free_Feature * feat, BAT_Attribute * attr)
{

  /*
   * if we already are a part of a gene or a transcript, we don't want
   * to unilatterally alter the id.
   */
  _BAT_Feature_Base_set_attribute_unchecked((BAT_Feature *) feat, attr);
}

void
BAT_Free_Feature_set_attribute_by_keyvalue(BAT_Free_Feature * feat,
					   const char *key, const char *val)
{
  _BAT_Feature_Base_set_attribute_by_keyvalue((BAT_Feature *) feat, key, val);
}

BAT_Attribute *
BAT_Free_Feature_remove_attribute(BAT_Free_Feature * feat, BAT_Attribute * attr,
				  int befrei)
{
  return _BAT_Feature_Base_remove_attribute((BAT_Feature *) feat, attr, befrei);
}


char *
BAT_Free_Feature_get_gene_id(BAT_Free_Feature * feat)
{
  BAT_Attribute *result;
  result = BAT_Free_Feature_find_attribute(feat, "gene_id");
  if (result == NULL)
    return "";
  return result->value;
}

char *
BAT_Free_Feature_get_transcript_id(BAT_Free_Feature * feat)
{
  BAT_Attribute *result;
  result = BAT_Free_Feature_find_attribute(feat, "transcript_id");
  if (result == NULL)
    return "";
  return result->value;
}

char
BAT_Free_Feature_get_frame_char(BAT_Free_Feature * feat)
{
  return _BAT_Feature_Base_get_frame_char((BAT_Feature *) feat);
}

/* NB: these ARE different in Feature and Free_Feature:
 *     we get the attribute a different way
 */
int
BAT_Free_Feature_gid_cmp(const void **f1, const void **f2)
{
  return strcmp(BAT_Free_Feature_get_gene_id(*((BAT_Free_Feature **) f1)),
		BAT_Free_Feature_get_gene_id(*((BAT_Free_Feature **) f2)));
}

int
BAT_Free_Feature_tid_cmp(const void **f1, const void **f2)
{
  return strcmp(BAT_Free_Feature_get_transcript_id(*((BAT_Free_Feature **) f1)),
		BAT_Free_Feature_get_transcript_id(*
						   ((BAT_Free_Feature **) f2)));
}

BAT_Attribute **
BAT_Free_Feature_get_attributes(BAT_Free_Feature * feat, da_pos_t *sz)
{
  return _BAT_Feature_Base_get_attributes((BAT_Feature *) feat, sz);
}

/* XXX not in base class.  too lazy. */
BAT_Attribute *
BAT_Free_Feature_find_attribute(BAT_Free_Feature * feat, const char *key)
{
  return darray_find(feat->attributes, key, BAT_Attribute_key_cmp);
}

/*
 * --- BAT_Comment ---
 */

/*
 * note when you add a comment to a BAT, you have to tell the feature that *
 * comes after it.  do comment parsing later?
 */
BAT_Comment *
BAT_Comment_new()
{
  BAT_Comment *cmt;

  cmt = malloc(sizeof(BAT_Comment));
  cmt->lines = NULL;
  cmt->lines = _BAT_Util_direct_renew_alloc(cmt->lines, Line_Allocator, 1);
  /*cmt = (BAT_Comment *) _BAT_Base_init(sizeof(BAT_Comment), 1, Line_Allocator);
   */

  cmt->offset = 0;
  cmt->inltext = NULL;

  return cmt;
}

/*
 * FIXME copying doesn't work.  I think the allocator should just have *
 * characters and not lines.
 */
BAT_Comment *
BAT_Comment_copy(BAT_Comment * cmt)
{
  BAT_Comment *copy;

  copy = malloc(sizeof(BAT_Comment));
  copy->lines = darray_copy(cmt->lines);
  /*
   * copy =
   * (BAT_Comment *) _BAT_Base_copy((BAT_Base *) cmt, sizeof(BAT_Comment),
   * Line_Allocator);
   */

  copy->offset = cmt->offset;
  copy->inltext = BAT_Util_copy_string(cmt->inltext);

  return copy;
}

void
BAT_Comment_delete(BAT_Comment * cmt, int befrei)
{
  /*_BAT_Base_delete((BAT_Base *) cmt);*/
  if (cmt->lines != NULL)
    darray_delete(cmt->lines, 1);

  cmt->offset = 0;

  if (cmt->inltext != NULL)
    free(cmt->inltext);

  if (befrei)
    free(cmt);
}

void
BAT_Comment_set_offset(BAT_Comment * cmt, int ofs)
{
  cmt->offset = ofs;
}

void
BAT_Comment_set_inline_text(BAT_Comment * cmt, const char *text)
{
  if (cmt->inltext != NULL)
    free(cmt->inltext);
  cmt->inltext = BAT_Util_copy_string(text);
}

void
BAT_Comment_add_line(BAT_Comment * cmt, const char *line)
{
  /*int sz, i; */
  char *linecpy;

  linecpy = BAT_Util_copy_string(line);
  darray_add(cmt->lines, (void *) linecpy);

  /*
   * sz = strlen(linecpy);
   * for (i = 0; i < sz; i++)
   * darray_add(cmt->_children, &linecpy[i]);
   */

  /*
   * char *str = BAT_Util_copy_string(line);
   * darray_add(cmt->_children, &str);
   */
}

/*
 * --- BAT_Attribute ---
 */

BAT_Attribute *
BAT_Attribute_new()
{
  BAT_Attribute *attr;

  attr = malloc(sizeof(BAT_Attribute));
  /*
   * attr = (BAT_Attribute *) _BAT_Base_init(sizeof(BAT_Attribute), 1,
   * Null_Allocator);
   */

  attr->key = NULL;
  attr->value = NULL;

  return attr;
}

BAT_Attribute *
BAT_Attribute_copy(BAT_Attribute * attr)
{
  BAT_Attribute *copy;

  copy = malloc(sizeof(BAT_Attribute));
  /*
   * copy = (BAT_Attribute *) _BAT_Base_copy((BAT_Base *) attr,
   * sizeof(BAT_Attribute), Null_Allocator, 1);
   */

  copy->key = BAT_Util_copy_string(attr->key);
  copy->value = BAT_Util_copy_string(attr->value);

  return copy;
}



void
BAT_Attribute_delete(BAT_Attribute * attr, int befrei)
{
  /*
   * _BAT_Base_delete((BAT_Base *) attr);
   */

  if (attr->key != NULL)
    free(attr->key);

  if (attr->value != NULL)
    free(attr->value);

  if (befrei)
    free(attr);
}

BAT_Attribute *
BAT_Attribute_new_gene_id()
{
  BAT_Attribute *attr;
  attr = malloc(sizeof(BAT_Attribute));
  attr->key = malloc((strlen("gene_id") + 1) * sizeof(char *));
  strcpy(attr->key, "gene_id");
  attr->value = NULL;
  return attr;
}

BAT_Attribute *
BAT_Attribute_new_transcript_id()
{
  BAT_Attribute *attr;
  attr = malloc(sizeof(BAT_Attribute));
  attr->key = malloc((strlen("transcript_id") + 1) * sizeof(char *));
  strcpy(attr->key, "transcript_id");
  attr->value = NULL;
  return attr;
}

int
BAT_Attribute_order(const void **p1, const void **p2)
{
  BAT_Attribute *a1 = *((BAT_Attribute **) p1), *a2 = *((BAT_Attribute **) p2);
  if (a1->key != NULL && !strcmp(a1->key, "gene_id"))
    return -1;
  if (a2->key != NULL && !strcmp(a2->key, "gene_id"))
    return 1;
  if (a1->key != NULL && !strcmp(a1->key, "transcript_id"))
    return -1;
  if (a2->key != NULL && !strcmp(a2->key, "transcript_id"))
    return 1;
  /* validated field ? */
  return 0;
}

int
BAT_Attribute_key_cmp(const void *p, const void *key)
{
  BAT_Attribute *attr = (BAT_Attribute *) p;
  if (attr->key != NULL)
    return strcmp(attr->key, key);
  return -1;
}

void
BAT_Attribute_set_all(BAT_Attribute * attr, const char *key, const char *value)
{
  BAT_Attribute_set_key(attr, key);
  BAT_Attribute_set_value(attr, value);
}

/*
 * XXX: these probably shouldn't copy out...
 */
void
BAT_Attribute_set_key(BAT_Attribute * attr, const char *key)
{
  if (attr->key != NULL)
    free(attr->key);
  attr->key = BAT_Util_copy_string(key);
}

void
BAT_Attribute_set_value(BAT_Attribute * attr, const char *value)
{
  if (attr->value != NULL)
    free(attr->value);
  attr->value = BAT_Util_copy_string(value);
}


BAT_State
_BAT_State_zero()
{
  BAT_State state = { 0, 0, 0, 0, 0, 0, 0, 0 };
  return state;
}

BAT_State
_BAT_State_union(BAT_State s1, BAT_State s2)
{
  BAT_State u;
  u.mod_genes = s1.mod_genes | s2.mod_genes;
  u.mod_transcripts = s1.mod_transcripts | s2.mod_transcripts;
  u.mod_features = s1.mod_features | s2.mod_features;
  u.mod_attributes = s1.mod_attributes | s2.mod_attributes;
  u.add_genes = s1.add_genes | s2.add_genes;
  u.add_transcripts = s1.add_transcripts | s2.add_transcripts;
  u.add_features = s1.add_features | s2.add_features;
  return u;
}

/*
 * --- BAT_Util ---
 */

char *
BAT_Util_copy_string(const char *str)
{
  char *copy;

  if (str != NULL) {
    copy = malloc((strlen(str) + 1) * sizeof(char));
    strcpy(copy, str);
    return copy;
  }
  else
    return NULL;
}

char *
BAT_Util_copy_char(const char *chr)
{
  char *copy;

  copy = malloc(sizeof(char));
  *copy = *chr;

  return copy;
}


void
_BAT_Util_renew_alloc(keyed_set_t * allocs, BAT_Allocator_ID id, int owner)
{
  darray_t *oldalloc, *newalloc;

  oldalloc = keyed_set_get(allocs, id);
  newalloc = _BAT_Util_direct_renew_alloc(oldalloc, id, owner);
  if (oldalloc == NULL)
    keyed_set_add(allocs, newalloc, id);
  else
    keyed_set_set(allocs, id, newalloc);
}

darray_t *
_BAT_Util_direct_renew_alloc(darray_t * alloc, BAT_Allocator_ID id, int owner)
{
  int old_sz;
  darray_ops_t *ops;

  if (owner)
    ops = darray_ops_copy(&BAT_Owner_Ops[id]);
  else
    ops = darray_ops_copy(&BAT_Passive_Ops[id]);

  if (alloc != NULL) {
    old_sz = darray_size(alloc);
    darray_delete(alloc, 1);
    return darray_new(old_sz + 5, ops);
  }
  else
    return darray_new(5, ops);
}
